/********************************************************************
	file name:	hy_spin_locker.h
	created:	2012/03/30 19:58
	author:		supercaco@sina.com
	purpose:	
*********************************************************************/
#ifndef _HY_SPIN_LOCK_H_
#define _HY_SPIN_LOCK_H_

#define SPINS_PER_YIELD     63

#ifdef _WIN32

	#include <windows.h>

	#define THREAD_ID			DWORD
	#define CURRENT_THREAD		GetCurrentThreadId()

class win32_mlock_t 
{
	win32_mlock_t():threadid(0),l(0),c(0){}

	inline int win32_acquire_lock () 
	{
		int spins = 0;
		for (;;) 
		{
			if (this->l != 0) 
			{
				if (this->threadid == CURRENT_THREAD) 
				{
					++this->c;
					return 0;
				}
			}
			else 
			{
				if (!InterlockedExchange(&this->l, 1)) 
				{
					hy_assert(!this->threadid);
					this->threadid = CURRENT_THREAD;
					this->c = 1;
					return 0;
				}
			}
			if ((++spins & SPINS_PER_YIELD) == 0)
				SleepEx(0, FALSE);
		}
	}

	inline void win32_release_lock () 
	{
		hy_assert(this->threadid == CURRENT_THREAD);
		hy_assert(this->l != 0);
		if (--this->c == 0) {
			this->threadid = 0;
			InterlockedExchange (&this->l, 0);
		}
	}

	inline int win32_try_lock () 
	{
		if (this->l != 0) 
		{
			if (this->threadid == CURRENT_THREAD) 
			{
				++this->c;
				return 1;
			}
		}
		else 
		{
			if (!InterlockedExchange(&this->l, 1))
			{
				hy_assert(!this->threadid);
				this->threadid = CURRENT_THREAD;
				this->c = 1;
				return 1;
			}
		}
		return 0;
	}

	volatile long l;
	unsigned int c;
	THREAD_ID threadid;
};

#else

#include <pthread.h>
#include <unistd.h>

#define THREAD_ID			pthread_t
#define CURRENT_THREAD      pthread_self()

/* Custom pthread-style spin locks on x86 and x64 for gcc */
class pthread_mlock_t 
{
	pthread_mlock_t():threadid(0),l(0),c(0){}

	inline int pthread_acquire_lock () 
	{
		int spins = 0;
		volatile unsigned int* lp = &this->l;
		for (;;) 
		{
			if (*lp != 0) 
			{
				if (this->threadid == CURRENT_THREAD) 
				{
					++this->c;
					return 0;
				}
			}
			else 
			{
				/* place args to cmpxchgl in locals to evade oddities in some gccs */
				int cmp = 0;
				int val = 1;
				int ret;
				__asm__ __volatile__  ("lock; cmpxchgl %1, %2"
					: "=a" (ret)
					: "r" (val), "m" (*(lp)), "0"(cmp)
					: "memory", "cc");
				if (!ret) {
					hy_assert(!this->threadid);
					this->threadid = CURRENT_THREAD;
					this->c = 1;
					return 0;
				}
			}
			if ((++spins & SPINS_PER_YIELD) == 0) 
			{
#if defined (__SVR4) && defined (__sun) /* solaris */
				thr_yield();
#else
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
				sched_yield();
#else  /* no-op yield on unknown systems */
				;
#endif /* __linux__ || __FreeBSD__ || __APPLE__ */
#endif /* solaris */
			}
		}
	}

	inline void pthread_release_lock () 
	{
		volatile unsigned int* lp = &this->l;
		hy_assert(*lp != 0);
		hy_assert(this->threadid == CURRENT_THREAD);
		if (--this->c == 0) {
			this->threadid = 0;
			int prev = 0;
			int ret;
			__asm__ __volatile__ ("lock; xchgl %0, %1"
				: "=r" (ret)
				: "m" (*(lp)), "0"(prev)
				: "memory");
		}
	}

	inline int pthread_try_lock () 
	{
		volatile unsigned int* lp = &this->l;
		if (*lp != 0) 
		{
			if (this->threadid == CURRENT_THREAD) 
			{
				++this->c;
				return 1;
			}
		}
		else 
		{
			int cmp = 0;
			int val = 1;
			int ret;
			__asm__ __volatile__  ("lock; cmpxchgl %1, %2"
				: "=a" (ret)
				: "r" (val), "m" (*(lp)), "0"(cmp)
				: "memory", "cc");
			if (!ret) {
				hy_assert(!this->threadid);
				this->threadid = CURRENT_THREAD;
				this->c = 1;
				return 1;
			}
		}
		return 0;
	}



	volatile unsigned int l;
	unsigned int c;
	THREAD_ID threadid;
};

#endif

#ifdef _WIN32
	typedef win32_mlock_t	hy_spin_lock;
#else
	typedef	pthread_mlock_t hy_spin_lock;
#endif


#endif  //_HY_SPIN_LOCK_H_
